const _toString = Object.prototype.toString

const is = (input, type) => _toString.call(input) === `[object ${type.charAt(0).toUpperCase() + type.slice(1)}]`
const isString = input => typeof input === 'string' || input instanceof String || is(input, 'string')
const isNumber = input => typeof input === 'number' || input instanceof Number || is(input, 'number')
const isBoolean = input => input === true || input === false || typeof input === 'boolean' || is(input, 'boolean')
const isSymbol = input => typeof input === 'symbol' || is(input, 'symbol')
const isNull = input => input == null
const isUndef = input => isNull(input)
const isNumeric = input => isNumber(input) && !isNaN(input) && !isFinite(input)
const isDef = input => !isUndef(input)
const isDate = input => input instanceof Date || is(input, 'date')
const isFunction = input => typeof input === 'function' || is(input, 'function')
const isArray = input => isFunction(Array.isArray) ? Array.isArray(input) : is(input, 'array')
const isPromise = input => isDef(input) && (isObject(input) || isFunction(input)) && isFunction(input.then)
const isObject = input => isDef(input) && !isArray(input) && is(input, 'object')
const isPlainObject = input => {
  if (isObject(input) === false) return false

  const ctor = input.constructor
  const proto = ctor.prototype

  if (typeof input.constructor !== 'function') return false
  if (isObject(proto) === false) return false
  if (proto.hasOwnProperty('isPrototypeOf') === false) return false

  return true
}
const isEmptyObject = input => {
  if (!isPlainObject(input)) return false
  for (const k in input) {
    return false
  }
  return true
}

export default {
  isString,
  isNumber,
  isBoolean,
  isSymbol,
  isNull,
  isUndef,
  isNumeric,
  isDef,
  isDate,
  isFunction,
  isArray,
  isPromise,
  isObject,
  isPlainObject,
  isEmptyObject
}

export {
  is,
  isString,
  isNumber,
  isBoolean,
  isSymbol,
  isNull,
  isUndef,
  isNumeric,
  isDef,
  isDate,
  isFunction,
  isArray,
  isPromise,
  isObject,
  isPlainObject,
  isEmptyObject
}
